package com.fast.mybatis.sqlprovider;

import java.text.MessageFormat;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fast.mybatis.constants.JavaType;
import com.fast.mybatis.exception.MapperException;
import com.fast.mybatis.mapper.holder.PojoMeta;
import com.fast.mybatis.mapper.holder.PojoMetaHolder;
import com.fast.mybatis.model.ColumnMeta;
import com.fast.mybatis.model.IdMeta;
import com.fast.mybatis.model.OrderByMeta;
import com.fast.mybatis.utils.ObjectUtil;
import com.fast.mybatis.utils.StringUtil;

public class MybatisProvider<T> {

	private static final Logger log = LoggerFactory.getLogger(MybatisProvider.class);
	
	private static final List<JavaType> javaTypeList = Arrays.asList(new JavaType[] {JavaType.DOUBLE,JavaType.FLOAT,JavaType.LONG,JavaType.INTEGER,JavaType.BIGDECIMAL});
	
	public String insert(final Object t) {
		try {
			StringBuilder sql = new StringBuilder();
			PojoMetaHolder holder = new PojoMetaHolder();
			PojoMeta pojoMeta =	holder.getMeta(t.getClass());
			sql.append(" INSERT INTO ");
			sql.append(pojoMeta.getTableName());
			sql.append("(");
			for(ColumnMeta c : pojoMeta.getColumnList()) {
				sql.append(c.getColumnName());
				sql.append(",");
			}
			
			if (sql.toString().endsWith(",")) {
				sql.delete(sql.length() - 1, sql.length());
			}
			sql.append(")");
			sql.append(" VALUES (");
			for(ColumnMeta c : pojoMeta.getColumnList()) {
				sql.append(getInsertColumn(c));
				sql.append(",");
			}
			if (sql.toString().endsWith(",")) {
				sql.delete(sql.length() - 1, sql.length());
			}
			sql.append(")");
			if (log.isDebugEnabled()) {
				log.debug("CommonProvider.insert.sql:{}",sql);
			}
			return String.valueOf(sql);
		}catch(MapperException e) {
			log.error(e.getMessage());
		}
		return null;
		
	}
	
	public String insertSelective(final Object t) {
		try {
			StringBuilder sql = new StringBuilder();
			PojoMetaHolder holder = new PojoMetaHolder();
			PojoMeta pojoMeta =	holder.getMeta(t.getClass());
			sql.append(" INSERT INTO ");
			sql.append(pojoMeta.getTableName());
			sql.append("(");
			for(ColumnMeta c : pojoMeta.getColumnList()) {
				if (isNotEmptyValue(t, c)) {
					continue;
				}
				sql.append(c.getColumnName());
				sql.append(",");
			}
			
			if (sql.toString().endsWith(",")) {
				sql.delete(sql.length() - 1, sql.length());
			}
			sql.append(")");
			sql.append(" VALUES (");
			for(ColumnMeta c : pojoMeta.getColumnList()) {
				if (isNotEmptyValue(t, c)) {
					continue;
				}
				sql.append(getInsertColumn(c));
				sql.append(",");
			}
			if (sql.toString().endsWith(",")) {
				sql.delete(sql.length() - 1, sql.length());
			}
			sql.append(")");
			if (log.isDebugEnabled()) {
				log.debug("CommonProvider.insertSelective.sql:{}",sql);
			}
			return String.valueOf(sql);
		}catch(MapperException e) {
			log.error(e.getMessage());
		}
		return null;
	}
	
	private boolean isNotEmptyValue(final Object t,final ColumnMeta c) {
		Object value = ObjectUtil.invokeMethod(t.getClass(), StringUtil.getMethod(c.getName()), null, t);
		return StringUtil.isEmpty(value) || (javaTypeList.contains(c.getJavaType()) && String.valueOf(value).equals("0"));
	}
	
	public String insertList(Map<String, List<T>> params) {
		try {
			List<T> list = params.get("list");
			if (list == null || list.size() <= 0) {
				return "";
			}
			Object t = list.get(0);
			StringBuilder sql = new StringBuilder();
			PojoMetaHolder holder = new PojoMetaHolder();
			PojoMeta pojoMeta =	holder.getMeta(t.getClass());
			sql.append(" INSERT INTO ");
			sql.append(pojoMeta.getTableName());
			sql.append("(");
			for(ColumnMeta c : pojoMeta.getColumnList()) {
				sql.append(c.getColumnName());
				sql.append(",");
			}
			
			if (sql.toString().endsWith(",")) {
				sql.delete(sql.length() - 1, sql.length());
			}
			sql.append(")");
			sql.append(" VALUES ");
			StringBuilder values = new StringBuilder();
			StringBuilder formatValues = new StringBuilder();
			for(ColumnMeta c : pojoMeta.getColumnList()) {
				
				values.append(getInsertList(c));
				values.append(",");
			}
			
			if (values.toString().endsWith(",")) {
				values.delete(values.length() - 1, values.length());
			}
			
			MessageFormat mf = new MessageFormat("(" + String.valueOf(values) + ")");
			for (int i = 0; i < list.size(); i++) {
				formatValues.append(mf.format(new Object[] { i }));
				formatValues.append(",");
			}
			
			
			
			if (sql.toString().endsWith(",")) {
				sql.delete(sql.length() - 1, sql.length());
			}
	
			if (formatValues.toString().endsWith(",")) {
				formatValues.delete(formatValues.length() - 1, formatValues.length());
			}
			sql.append(formatValues);
			if (log.isDebugEnabled()) {
				log.debug("CommonProvider.insertList.sql:{}",sql);
			}
			return String.valueOf(sql);
		}catch(MapperException e) {
			log.error(e.getMessage());
		}
		return null;
	}
	
	
	
	public String insertListSelective(final Map<String, List<T>> params) {
		try {
			List<T> list = params.get("list");
			if (list == null || list.size() <= 0) {
				return "";
			}
			Object t = list.get(0);
			StringBuilder sql = new StringBuilder();
			PojoMetaHolder holder = new PojoMetaHolder();
			PojoMeta pojoMeta =	holder.getMeta(t.getClass());
			sql.append(" INSERT INTO ");
			sql.append(pojoMeta.getTableName());
			sql.append("(");
			for(ColumnMeta c : pojoMeta.getColumnList()) {
				if (isNotEmptyValue(t, c)) {
					continue;
				}
				sql.append(c.getColumnName());
				sql.append(",");
			}
			
			if (sql.toString().endsWith(",")) {
				sql.delete(sql.length() - 1, sql.length());
			}
			sql.append(")");
			sql.append(" VALUES ");
			StringBuilder values = new StringBuilder();
			StringBuilder formatValues = new StringBuilder();
			for(ColumnMeta c : pojoMeta.getColumnList()) {
				if (isNotEmptyValue(t, c)) {
					continue;
				}
				values.append(getInsertList(c));
				values.append(",");
			}
			
			if (values.toString().endsWith(",")) {
				values.delete(values.length() - 1, values.length());
			}
			
			MessageFormat mf = new MessageFormat("(" + String.valueOf(values) + ")");
			for (int i = 0; i < list.size(); i++) {
				formatValues.append(mf.format(new Object[] { i }));
				formatValues.append(",");
			}
			
			
			
			if (sql.toString().endsWith(",")) {
				sql.delete(sql.length() - 1, sql.length());
			}
	
			if (formatValues.toString().endsWith(",")) {
				formatValues.delete(formatValues.length() - 1, formatValues.length());
			}
			sql.append(formatValues);
			if (log.isDebugEnabled()) {
				log.debug("CommonProvider.insertListSelective.sql:{}",sql);
			}
			return String.valueOf(sql);
		}catch(MapperException e) {
			log.error(e.getMessage());
		}
		return null;
	}
	
	private String getInsertList(final ColumnMeta fieldMeta) {
		StringBuilder sql = new StringBuilder();
		sql.append("'#{'");
		sql.append("list[{0}]." + fieldMeta.getName() + ",");
		sql.append("javaType=" + getJavaType(fieldMeta) + ",");
		sql.append("jdbcType=" + getJdbcType(fieldMeta));
		sql.append("}");
		return String.valueOf(sql);
	}
	
	private String getInsertColumn(final ColumnMeta c) {
		StringBuilder sql = new StringBuilder();
		sql.append("#{");
		sql.append(c.getName());
		sql.append(",");
		sql.append("javaType=" + getJavaType(c));
		sql.append(",");
		sql.append("jdbcType=" + getJdbcType(c));
		sql.append("}");
		return String.valueOf(sql);
	}
	
	public String updateByPrimaryKey(final Object t) {
		StringBuilder sql = new StringBuilder();
		PojoMetaHolder holder = new PojoMetaHolder();
		PojoMeta pojoMeta =	holder.getMeta(t.getClass());
		sql.append("UPDATE ");
		sql.append(pojoMeta.getTableName());
		sql.append(" SET ");
		for(ColumnMeta c : pojoMeta.getColumnList()) {
			if(isPrimary(pojoMeta, c)) {
				continue;
			}
			sql.append(getWhereColumn(c));
			sql.append(",");
		}
		if (sql.toString().endsWith(",")) {
			sql.delete(sql.length() - 1, sql.length());
		}
		sql.append(" WHERE ");
		for(IdMeta id : pojoMeta.getPrimaryList()) {
			sql.append(getWhereColumn(id));
			sql.append(" AND");
		}
		
		if (sql.toString().endsWith("AND")) {
			sql.delete(sql.length() - 3, sql.length());
		}
		if (log.isDebugEnabled()) {
			log.debug("CommonProvider.updateByPrimaryKey.sql:{}",sql);
		}
		return String.valueOf(sql);
	}
	
	
	public String updateByPrimaryKeySelective(final Object t) {
		StringBuilder sql = new StringBuilder();
		PojoMetaHolder holder = new PojoMetaHolder();
		PojoMeta pojoMeta =	holder.getMeta(t.getClass());
		sql.append("UPDATE ");
		sql.append(pojoMeta.getTableName());
		sql.append(" SET ");
		for(ColumnMeta c : pojoMeta.getColumnList()) {
			if (isNotEmptyValue(t, c) || isPrimary(pojoMeta, c)) {
				continue;
			}
			sql.append(getWhereColumn(c));
			sql.append(",");
		}
		if (sql.toString().endsWith(",")) {
			sql.delete(sql.length() - 1, sql.length());
		}
		
		sql.append(" WHERE ");
		for(IdMeta id : pojoMeta.getPrimaryList()) {
			sql.append(getWhereColumn(id));
			sql.append(" AND");
		}
		
		if (sql.toString().endsWith("AND")) {
			sql.delete(sql.length() - 3, sql.length());
		}
		if (log.isDebugEnabled()) {
			log.debug("CommonProvider.updateByPrimaryKeySelective.sql:{}",sql);
		}
		return String.valueOf(sql);
	}
	
	private boolean isPrimary(final PojoMeta pojoMeta,final ColumnMeta column) {
		for(IdMeta id : pojoMeta.getPrimaryList()) {
			if(id.getColumnMeta().getName().equals(column.getName())) {
				return true;
			}
		}
		return false;
	}
	

	private String getWhereColumn(final IdMeta fieldMeta) {
		StringBuilder sql = new StringBuilder();
		sql.append(" ");
		sql.append(fieldMeta.getColumnMeta().getColumnName());
		sql.append("=");
		sql.append("#{");
		sql.append(fieldMeta.getColumnMeta().getName());
		sql.append(",");
		sql.append("javaType=" + getJavaType(fieldMeta.getColumnMeta()));
		sql.append(",");
		sql.append("jdbcType=" + getJdbcType(fieldMeta.getColumnMeta()));
		sql.append("}");
		return String.valueOf(sql);
	}
	
	private String getWhereColumn(final ColumnMeta fieldMeta) {
		StringBuilder sql = new StringBuilder();
		sql.append(" ");
		sql.append(fieldMeta.getColumnName());
		sql.append("=");
		sql.append("#{");
		sql.append(fieldMeta.getName());
		sql.append(",");
		sql.append("javaType=" + getJavaType(fieldMeta));
		sql.append(",");
		sql.append("jdbcType=" + getJdbcType(fieldMeta));
		sql.append("}");
		return String.valueOf(sql);
	}
	
	private String getJavaType(final ColumnMeta fieldMeta) {
		if (fieldMeta == null) {
			return "string";
		}
		return String.valueOf(fieldMeta.getJavaType().value);
	}

	private String getJdbcType(final ColumnMeta fieldMeta) {
		if (fieldMeta == null) {
			return "VARCHAR";
		}
		return String.valueOf(fieldMeta.getJdbcType());
	}
	
	public String deleteByPrimaryKey(final Object t) {
		try {
			StringBuilder sql = new StringBuilder();
			PojoMetaHolder holder = new PojoMetaHolder();
			PojoMeta pojoMeta =	holder.getMeta(t.getClass());
			sql.append("DELETE FROM ");
			sql.append(pojoMeta.getTableName());
			sql.append(" WHERE ");
			for(IdMeta id : pojoMeta.getPrimaryList()) {
				sql.append(getWhereColumn(id));
				sql.append(" AND");
			}
			
			if (sql.toString().endsWith("AND")) {
				sql.delete(sql.length() - 3, sql.length());
			}
			if (log.isDebugEnabled()) {
				log.debug("CommonProvider.deleteByPrimaryKey.sql:{}",sql);
			}
			return String.valueOf(sql);
		}catch(MapperException e) {
			log.error(e.getMessage());
		}
		return null;
	}
	
	public String countAll(final Object t){
		try {
			StringBuilder sql = new StringBuilder();
			PojoMetaHolder holder = new PojoMetaHolder();
			PojoMeta pojoMeta =	holder.getMeta(t.getClass());
			sql.append(" SELECT COUNT(1) FROM ");
			sql.append(pojoMeta.getTableName());
			sql.append(" WHERE 1=1 ");
			for(ColumnMeta c : pojoMeta.getColumnList()) {
				if(isNotEmptyValue(t, c)) {
					continue;
				}
				sql.append(" AND");
				sql.append(getWhereColumn(c));
				sql.append(",");
			}
			if (sql.toString().endsWith(",")) {
				sql.delete(sql.length() - 1, sql.length());
			}
			if (log.isDebugEnabled()) {
				log.debug("CommonProvider.countAll.sql:{}",sql);
			}
			return String.valueOf(sql);
		}catch(MapperException e) {
			log.error(e.getMessage());
		}
		return null;
	}
	
	public String selectByPrimaryKey(final Object t) {
		try {
			StringBuilder sql = new StringBuilder();
			PojoMetaHolder holder = new PojoMetaHolder();
			PojoMeta pojoMeta =	holder.getMeta(t.getClass());
			sql.append(" SELECT ");
			for(ColumnMeta c : pojoMeta.getColumnList()) {
				sql.append(c.getColumnName());
				sql.append(" "+c.getName());
				sql.append(",");
			}
			
			if (sql.toString().endsWith(",")) {
				sql.delete(sql.length() - 1, sql.length());
			}
			sql.append(" FROM ");
			sql.append(pojoMeta.getTableName());
			sql.append(" WHERE 1=1 ");
			for(IdMeta id : pojoMeta.getPrimaryList()) {
				sql.append(" AND");
				sql.append(getWhereColumn(id));
			}
			
			if (sql.toString().endsWith("AND")) {
				sql.delete(sql.length() - 3, sql.length());
			}
			if (log.isDebugEnabled()) {
				log.debug("CommonProvider.selectByPrimaryKey.sql:{}",sql);
			}
			return String.valueOf(sql);
		}catch(MapperException e) {
			log.error(e.getMessage());
		}
		return null;
	}
	
	public String select(final Object t) {
		try {
			StringBuilder sql = new StringBuilder();
			PojoMetaHolder holder = new PojoMetaHolder();
			PojoMeta pojoMeta =	holder.getMeta(t.getClass());
			sql.append(" SELECT ");
			for(ColumnMeta c : pojoMeta.getColumnList()) {
				sql.append(c.getColumnName());
				sql.append(" "+c.getName());
				sql.append(",");
			}
			
			if (sql.toString().endsWith(",")) {
				sql.delete(sql.length() - 1, sql.length());
			}
			sql.append(" FROM ");
			sql.append(pojoMeta.getTableName());
			sql.append(" WHERE 1=1 ");
			for(ColumnMeta c : pojoMeta.getColumnList()) {
				if(isNotEmptyValue(t, c)) {
					continue;
				}
				sql.append(" AND");
				sql.append(getWhereColumn(c));
			}
			if (sql.toString().endsWith("AND")) {
				sql.delete(sql.length() - 3, sql.length());
			}
			
			for(OrderByMeta c : pojoMeta.getOrderByList()) {
				sql.append(" ORDER BY  ");
				sql.append(c.getColumnMeta().getColumnName());
				sql.append(" " + c.getOrderBy().name());
				
			}
			
			if (log.isDebugEnabled()) {
				log.debug("CommonProvider.select.sql:{}",sql);
			}
			return String.valueOf(sql);
		}catch(MapperException e) {
			log.error(e.getMessage());
		}
		return null;
	}
	
	public String selectIn(final List<Object> t) {
		try {
			StringBuilder sql = new StringBuilder();
			PojoMetaHolder holder = new PojoMetaHolder();
			PojoMeta pojoMeta =	holder.getMeta(t.getClass());
			sql.append(" SELECT ");
			for(ColumnMeta c : pojoMeta.getColumnList()) {
				sql.append(c.getColumnName());
				sql.append(" "+c.getName());
				sql.append(",");
			}
			
			if (sql.toString().endsWith(",")) {
				sql.delete(sql.length() - 1, sql.length());
			}
			sql.append(" FROM ");
			sql.append(pojoMeta.getTableName());
			sql.append(" WHERE 1=1 ");
			for(ColumnMeta c : pojoMeta.getColumnList()) {				
				sql.append(" AND");
				sql.append(getWhereColumn(c));

			}
			if (sql.toString().endsWith("AND")) {
				sql.delete(sql.length() - 3, sql.length());
			}
			if (log.isDebugEnabled()) {
				log.debug("CommonProvider.selectIn.sql:{}",sql);
			}
			return String.valueOf(sql);
		}catch(MapperException e) {
			log.error(e.getMessage());
		}
		return null;
	}
	
}
